﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;

namespace DiscMaster.Web.Models
{
    public class DiscMasterRepository : DiscMaster.Web.Models.IDiscMasterRepository
    {
        private DiscMasterDataContext db = new DiscMasterDataContext();

        // Query Methods: Matches
        public IQueryable<Match> FindAllMatches()
        {
            return db.Matches;
        }
        public IQueryable<Match> FindAllMatches(Guid id)
        {
            return FindAllMatches(id.ToString());
        }
        public IQueryable<Match> FindAllMatches(String id)
        {
            return db.Matches.Where(m => m.MatchPlayers.Any(mp => mp.playerid.Equals(id)));
        }
        public IQueryable<Match> FindUpcomingMatches()
        {
            return db.Matches.Where(m=>m.matchdate>DateTime.Now);
        }
        public IQueryable<Match> FindPastMatches()
        {
            return db.Matches.Where(m=>m.matchdate<DateTime.Now);
        }
        public IQueryable<MatchResult> FindAllMatchResults()
        {
            return db.MatchResults;
        }
        public IQueryable<MatchResult> FindAllMatchResultsByPlayer(Player player)
        {
            return player.MatchResults.Where(mr => mr.playerid.Equals(player.UserId)).Where(mr => mr.result > 0).AsQueryable();
        }

        // Query Methods: Players
        public IQueryable<Player> FindAllPlayers()
        {
            return db.Players;
        }
        public IQueryable<Friend> FindAllFriends(Guid id)
        {
            return db.Players.Single(p=>p.UserId==id).Friends.AsQueryable();
        }

        // Query Methods: Clubs
        public IQueryable<Club> FindAllClubs()
        {
            return db.Clubs;
        }

        // Query Methods: Courses
        public IQueryable<Course> FindAllCourses()
        {
            return db.Courses;
        }
        public IQueryable<CourseLayout> FindAllCourseLayouts()
        {
            return db.CourseLayouts;
        }
        public IQueryable<Hole> FindAllHoles()
        {
            return db.Holes;
        }

        public Forecast GetForecast(Guid courseid, int forecastindex)
        {
            return GetForecast(courseid.ToString(), forecastindex);
        }
        public Forecast GetForecast(string courseid, int forecastindex)
        {
            List<Forecast> forecasts = GetForecasts(courseid);

            if (forecasts!=null)
            {
                return forecasts[forecastindex];
            }

            return null;
        }
        public List<Forecast> GetForecasts(string courseid)
        {
            var course = GetCourse(courseid);

            if (!String.IsNullOrEmpty(course.CourseDetail.yr_weather))
            {
                List<Forecast> forecasts = null;

                // determine if the forecast is already stored in the cache..
                if (System.Web.HttpContext.Current.Cache[System.Web.HttpContext.Current.Server.UrlPathEncode(course.CourseDetail.yr_weather)] != null)
                {
                    // .. if so, fetch it
                    forecasts = (List<Forecast>)System.Web.HttpContext.Current.Cache[System.Web.HttpContext.Current.Server.UrlPathEncode(course.CourseDetail.yr_weather)];
                }
                else
                {
                    // .. and if not, try to consume the source using a reader..
                    try
                    {

                        // used to read the forecast xml
                        System.Xml.XmlDocument doc = new XmlDocument();
                        // our soon-to-be forecast node collection
                        System.Xml.XmlNodeList nodelist = null;

                        System.Xml.XmlTextReader reader = new XmlTextReader(System.Web.HttpContext.Current.Server.UrlPathEncode(course.CourseDetail.yr_weather));
                        doc.Load(reader);

                        // if all is loaded ok and data is there, simple xpath query to get the forecast data..
                        if (doc != null)
                        {
                            if (doc.HasChildNodes)
                            {
                                nodelist = doc.SelectNodes("/weatherdata/forecast/tabular/time");
                            }
                        }

                        if (nodelist != null)
                        {
                            if (nodelist.Count > 0)
                            {
                                // iterate each forecast in the current group (day)

                                var forecastscol = from node in nodelist.OfType<XmlNode>()
                                            select new Forecast
                                            {
                                                DateFrom = DateTime.Parse(node.Attributes["from"].Value).ToString("yyyy-MM-dd HH:MM"),
                                                DateTo = DateTime.Parse(node.Attributes["to"].Value).ToString("yyyy-MM-dd HH:MM"),
                                                Conditions = node["symbol"].Attributes["name"].Value,
                                                SymbolNumber = node["symbol"].Attributes["number"].Value.PadLeft(2, '0'),
                                                Precipitation = node["precipitation"].Attributes["value"].Value,
                                                Temperature = node["temperature"].Attributes["value"].Value,
                                                Wind = node["windSpeed"].Attributes["name"].Value,
                                                WindDirection = node["windDirection"].Attributes["code"].Value,
                                                WindSpeed = node["windSpeed"].Attributes["mps"].Value
                                            };
                                forecasts = forecastscol.ToList();

                                foreach (Forecast forecast in forecasts)
                                {
                                    forecast.ForecastsCount = forecasts.Count();
                                    forecast.ForecastIndex = forecasts.IndexOf(forecast);
                                    if (forecast.ForecastIndex == forecast.ForecastsCount-1)
                                    {
                                        forecast.NextForecastIndex = 0;
                                    }
                                    else
                                    {
                                        forecast.NextForecastIndex = forecast.ForecastIndex + 1;
                                    }
                                }

                                // add entry to cache
                                System.Web.HttpContext.Current.Cache.Add(System.Web.HttpContext.Current.Server.UrlPathEncode(course.CourseDetail.yr_weather), forecasts, null, DateTime.Now.AddMinutes(60), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Default, null);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // just in case something went wrong we store a blank list for 5 mins..
                        forecasts = new List<Forecast>();
                        System.Web.HttpContext.Current.Cache.Add(System.Web.HttpContext.Current.Server.UrlPathEncode(course.CourseDetail.yr_weather), forecasts, null, DateTime.Now.AddMinutes(5), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Default, null);
                    }
                }
                if (forecasts != null)
                {
                    return forecasts;
                }
            }
            return null;
        }

        public Match GetMatch(Guid id)
        {
            return db.Matches.Single(m=>m.matchid==id);
        }
        public Match GetMatch(string id)
        {
            try
            {
                return db.Matches.Single(m => m.matchid.Equals(id));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        internal Match GetMatchByName(string name)
        {
            try
            {
                return db.Matches.Single(m => m.name.Equals(name));
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public Player GetPlayer(Guid id)
        {
            try
            {
                return db.Players.Single(p => p.UserId == id);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Player GetPlayer(string id)
        {
            try
            {
                return db.Players.Single(p => p.UserId.Equals(id));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Player GetLoggedOnPlayer()
        {
            return GetPlayer(System.Web.Security.Membership.Provider.GetUser(System.Web.HttpContext.Current.User.Identity.Name, false).ProviderUserKey.ToString());
        }
        public Player GetPlayerByName(string name)
        {
            try
            {
                return db.Players.Single(p => p.UserName.Equals(name));
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public Course GetCourse(Guid id)
        {
            try
            {
                return db.Courses.Single(c => c.courseid == id);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Course GetCourse(string id)
        {
            try
            {
                return db.Courses.Single(c => c.courseid.Equals(id));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Course GetCourseByName(string name)
        {
            try
            {
                return db.Courses.Single(c => c.name.Equals(name));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public CourseLayout GetCourseLayout(Guid id)
        {
            try
            {
                return db.CourseLayouts.Single(c => c.layoutid == id);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public CourseLayout GetCourseLayout(string id)
        {
            try
            {
                return db.CourseLayouts.Single(c => c.layoutid.Equals(id));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public CourseLayout GetCourseLayoutByName(string name)
        {
            try
            {
                return db.CourseLayouts.Single(c => c.name.Equals(name));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Club GetClub(Guid id)
        {
            try
            {
                return db.Clubs.Single(c => c.clubid == id);
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Club GetClub(string id)
        {
            try
            {
                return db.Clubs.Single(c => c.clubid.Equals(id));
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public Club GetClubByName(string name)
        {
            try
            {
                return db.Clubs.Single(c => c.name.Equals(name));
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        // Insert/Delete
        public void AddMatch(Match match)
        {
            db.Matches.InsertOnSubmit(match);
        }
        public void DeleteMatch(Match match)
        {
            db.MatchResults.DeleteAllOnSubmit(match.MatchResults);
            db.MatchPlayers.DeleteAllOnSubmit(match.MatchPlayers);
            db.Matches.DeleteOnSubmit(match);
        }

        public void AddPlayer(Player player)
        {
            db.Players.InsertOnSubmit(player);
        }
        public void DeletePlayer(Player player)
        {
            db.MatchResults.DeleteAllOnSubmit(player.MatchResults);
            db.MatchPlayers.DeleteAllOnSubmit(player.MatchPlayers);
            db.Friends.DeleteAllOnSubmit(player.Friends);
            db.Friends.DeleteAllOnSubmit(player.Friends1);
            db.ClubMembers.DeleteAllOnSubmit(player.ClubMembers);
            db.PlayerDetails.DeleteOnSubmit(player.PlayerDetail);
        }

        public void AddCourse(Course course)
        {
            db.Courses.InsertOnSubmit(course);
        }
        public void DeleteCourse(Course course)
        {
            db.CourseLayouts.DeleteAllOnSubmit(course.CourseLayouts);
            db.CourseDetails.DeleteOnSubmit(course.CourseDetail);
            db.Courses.DeleteOnSubmit(course);
        }

        public void AddCourseLayout(CourseLayout courselayout)
        {
            db.CourseLayouts.InsertOnSubmit(courselayout);
        }
        public void DeleteCourseLayout(CourseLayout courselayout)
        {
            db.Holes.DeleteAllOnSubmit(courselayout.Holes);
            db.CourseLayouts.DeleteOnSubmit(courselayout);
        }

        public void AddClub(Club club)
        {
            db.Clubs.InsertOnSubmit(club);
        }
        public void DeleteClub(Club club)
        {
            db.ClubMembers.DeleteAllOnSubmit(club.ClubMembers);
            db.Clubs.DeleteOnSubmit(club);
        }

        public void AddClubMember(Club club, Player player)
        {
            db.ClubMembers.InsertOnSubmit(new ClubMember() { clubid = club.clubid, playerid = player.UserId, approved = false });
        }
        public void UpdateClubMemberStatus(Club club, Player player, bool status)
        {
            db.ClubMembers.Single(cm => cm.clubid == club.clubid & cm.playerid == player.UserId).approved=status;
        }
        public void RemoveClubMember(Club club, Player player)
        {
            db.ClubMembers.DeleteOnSubmit(club.ClubMembers.Single(cm => cm.clubid == club.clubid & cm.playerid == player.UserId));
        }
        // Persistence
        public void Save()
        {
            db.SubmitChanges();
        }
    }
}
